    package connect3.team7.util;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import java.io.*;
import java.util.*;
import java.net.*;

import connect3.team7.game.*;
import connect3.team7.util.Host;

/*

   Singleton HostController class
   ------------------------------
   - Registers itself with the RMIRegistry

   - Exposes certain methods remotely to client players

*/

public class HostController implements Host {
  private static HostController instance;

  private Registry registry;
  private Host     remoteObj;
  private Host     stub;
  private String   bind_name;
  private Hashtable<String, Client> client_rmi_hash;

  private String client_lookup;

  public static HostController getInstance(HostConnectionSettings hcs) {
    if (instance == null)
      instance = new HostController(hcs);

    return instance;  
  }

  private HostController(HostConnectionSettings hcs) {
    super();
    bind_name = "HostController";
    client_lookup = "ClientController";
    registerHost(hcs);
    client_rmi_hash = new Hashtable<String, Client>();
  }

  private void registerHost(HostConnectionSettings host) {
    try {
      System.setProperty("java.rmi.server.hostname", host.getIP()); 
      System.setProperty("java.rmi.server.codebase", ConfigProperties.getConfigProperties().getProperty("rmi.codebase"));
      registry = LocateRegistry.createRegistry(host.getPort());
      
      stub     = (Host) UnicastRemoteObject.exportObject(this,host.getPort()); 
      registry.rebind(bind_name, stub);
      System.out.println("HostController registered!");

      DBController db = DBController.getInstance();
      db.insertHost(host);
      System.out.println("Host listing updated!");
    } catch(Exception e) {
      System.err.println("HostController exception: ");
      e.printStackTrace();
    }
  }

  public synchronized boolean join(Player p, Client c) {
    boolean ret = false;
    
    if (client_rmi_hash.containsKey(p.getName())) {
        ret = false;
    } else {
        client_rmi_hash.put(p.getName(), c);
        broadcastNewClient(p);
        ret = true;
    }
    
    return ret;
  }
  
  public synchronized Enumeration<String> getActivePlayers() {
      Enumeration<String> e = client_rmi_hash.keys();
      
      return e;
  }

  public synchronized void receiveClientMove(Token t) {
      /*
          1.  get Gameboard instance
          2.  apply the token to the local gameboard
          3.  update the GUI
       */
      
      System.out.println("Received move (" + t.getX() + "," + t.getY() + ") from " + t.getPlayerName());

    broadcastMove(t); 
  }

  private void broadcastMove(Token t) {
    Client   clientRemoteObj;
    Enumeration<String> e = client_rmi_hash.keys();

    System.out.println(client_rmi_hash.size() + " awaiting broadcast ...");

    while (e.hasMoreElements()) {
      String curr = e.nextElement();

      if (!curr.equals(t.getPlayerName())) {
        clientRemoteObj = client_rmi_hash.get(curr);
        
        try {
          System.out.println("broadcasting to ... " + curr);
          clientRemoteObj.syncGameBoardFromHost(t);
        } catch (RemoteException ex) {
          ex.printStackTrace();
        }
      }
    } 
  }
  
  private void broadcastNewClient(Player p) {
      Client clientRemoteObj;
      Enumeration<String> e = client_rmi_hash.keys();
      
      while (e.hasMoreElements()) {
          String curr = e.nextElement();
          
          if (!curr.equals(p.getName())) {
              clientRemoteObj = client_rmi_hash.get(curr);
              //clientRemoteObj.updateActivePlayers(p.getName());
          }
      }
  }
}

